package cn.genmer.test.security.algorithm.cas;


import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.LockSupport;

/**
 * 库存超卖 cas https://blog.csdn.net/weixin_43465669/article/details/104900044
 */
@Data
@Slf4j
public class MyLock {
    //volatile让其他线程可见
    private volatile int state=0;
    private Thread lockHolder;
    private static final Unsafe unsafe=UnsafeInstance.getInstance();
    //状态值的偏移量
    private static  long stateOffset;
    //用于存放排队中的线程
    private ConcurrentLinkedQueue<Thread> waiters=new ConcurrentLinkedQueue<>();
    static {
        try {
            stateOffset=unsafe.objectFieldOffset(MyLock.class.getDeclaredField("state"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void lock() {
        Thread thread = Thread.currentThread();
        //加锁成功
        if(aquire()){
            return;
        }
        waiters.add(thread);//加入到等待队列
        //自旋
        for(;;){
            //等待池中第一个线程获取到锁，移出池子
            if(waiters.peek()==thread&&aquire()){
                waiters.poll();
                break;
            }
            //阻塞当前线程
            LockSupport.park(thread);//保存对当前线程的引用

        }

    }
    public boolean aquire(){
        //任意时刻只能有1个线程加锁成功，cas比较与交换算法
        Thread thread = Thread.currentThread();
        //初始状态
        int c=getState();
        if(c==0){
            //同步器还未被持有(第二个条件容易遗忘==》其他线程全都阻塞)
            if((waiters.size()==0||thread==waiters.peek())&&compareAndSwapState(0,1)){
                log.info("第一个线程加锁");
                setLockHolder(thread);
                return true;
            }
        }
        return false;
    }

    public void unlock() {
        if(Thread.currentThread()!=lockHolder){
            throw new RuntimeException("当前线程不是锁的持有者");
        }
        int state = getState();
        if(compareAndSwapState(state,0)){
            setLockHolder(null);
            //把等待池中第一个线程设为锁
            Thread first = waiters.peek();
            if(first!=null){
                LockSupport.unpark(first);//唤醒第一个线程
            }

        }
    }
    public final boolean compareAndSwapState(int except,int update){
        //当前对象，某个字段的偏移量，期望值，修改值
        //修改成功返回true
        return unsafe.compareAndSwapInt(this,stateOffset,except,update);
    }
}

